package org.light.reflecttable.serviceimpl;

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.light.domain.Domain;
import org.light.domain.Dropdown;
import org.light.domain.FieldSerialComparator;
import org.light.domain.ManyToMany;
import org.light.reflecttable.dao.FieldDao;
import org.light.reflecttable.dao.TableDao;
import org.light.reflecttable.daoimpl.FieldDaoImpl;
import org.light.reflecttable.daoimpl.TableDaoImpl;
import org.light.reflecttable.database.DBConf;
import org.light.reflecttable.domain.Field;
import org.light.reflecttable.domain.Table;
import org.light.reflecttable.service.TableService;
import org.light.reflecttable.utils.ReflectUtil;
import org.light.shiroauth.ShiroAuthModule;
import org.light.simpleauth.SimpleAuthModule;
import org.light.utils.DomainUtil;
import org.light.utils.StringUtil;

public class TableServiceImpl implements TableService{
	protected TableDao dao = new TableDaoImpl();
	protected FieldDao fieldDao = new FieldDaoImpl();
	public static String dbLink = "jdbc:mysql://localhost:3306/";
	public static String dbHost = "localhost";
	public static String dbPort = "3306";
	public static String dbName = "";
	public static String userName = "jerry";
	public static String password = "jerry";
	public static String dbType = "MariaDB";
	public static int topn = -1;
			
	public List<Table> listAllTables() throws Exception{
		String dbLink = TableServiceImpl.dbLink;
		String dbHost = TableServiceImpl.dbHost;
		String dbPort = TableServiceImpl.dbPort;
		String dbName = TableServiceImpl.dbName;
		String userName = TableServiceImpl.userName;
		String password = TableServiceImpl.password;
		try (Connection connection = DBConf.initDB(dbHost,dbPort,dbName,userName,password)) {
			return dao.listAllTables(connection);
		}
	}
	
	@Override
	public Domain reflectTableToDomain(String tableName,String dbType) throws Exception{
		String dbLink = TableServiceImpl.dbLink;
		String dbHost = TableServiceImpl.dbHost;
		String dbPort = TableServiceImpl.dbPort;
		String dbName = TableServiceImpl.dbName;
		String userName = TableServiceImpl.userName;
		String password = TableServiceImpl.password;
		int topn = TableServiceImpl.topn;
		if (dbType.equalsIgnoreCase("MariaDB")||dbType.equalsIgnoreCase("MySQL")) {
			try (Connection connection = DBConf.initDB(dbHost,dbPort,dbName,userName,password)) {
				Table table = new Table();
				table.setTableName(tableName);
				table.setLabel(dao.findTableComment(connection, tableName, dbName));
				List<Field> fields = fieldDao.listAllTableFieldsByTableName(connection,tableName,dbName);
				Domain domain = new Domain();
				Set<org.light.domain.Field> dFields = new TreeSet<>(new FieldSerialComparator());
				for (Field f:fields) {
					org.light.domain.Field df = new org.light.domain.Field();
					df.setFieldName(StringUtil.changeTableColumtoDomainField(f.getFieldName()));
					df.setFixedName(StringUtil.changeTableColumtoDomainField(f.getFieldName()));
					df.setFieldType(ReflectUtil.lookupRustTypeByMySqlType(f.getFieldType()));
					df.setLabel(f.getLabel());
					df.setFieldKey(f.getFieldKey());
					df.setFieldNull(f.getFieldNull());
					df.setFieldExtra(f.getFieldExtra());
					df.setSerial(f.getId());
					dFields.add(df);
				}
				domain.setDbType(dbType);
				domain.setFields(dFields);
				domain.setPlural(tableName);
				domain.setLabel(table.getLabel());
				return domain;
			}
		} else if (dbType.equalsIgnoreCase("PostgreSQL")||dbType.equalsIgnoreCase("pgsql")) {
			try (Connection connection = DBConf.initPgDB(dbHost,dbPort,dbName,userName,password)) {
				Table table = new Table();
				table.setTableName(tableName);
				table.setLabel(dao.findPgTableComment(connection, tableName, dbName));
				List<Field> fields = fieldDao.listAllPgTableFieldsByTableName(connection,tableName);
				Domain domain = new Domain();
				Set<org.light.domain.Field> dFields = new TreeSet<>(new FieldSerialComparator());
				for (Field f:fields) {
					org.light.domain.Field df = new org.light.domain.Field();
					df.setFieldName(StringUtil.changeTableColumtoDomainField(f.getFieldName()));
					df.setFixedName(StringUtil.changeTableColumtoDomainField(f.getFieldName()));
					df.setFieldType(ReflectUtil.lookupRustTypeByPostgreSqlType(f.getFieldType()));
					df.setFieldKey(f.getFieldKey());
					df.setFieldNull(f.getFieldNull());
					df.setFieldDefault(f.getFieldDefault());
					df.setLabel(f.getLabel());
					df.setSerial(f.getId());
					dFields.add(df);
				}
				domain.setDbType(dbType);
				domain.setFields(dFields);
				domain.setPlural(tableName);
				domain.setLabel(table.getLabel());
				return domain;
			}
		}
		return null;
	}


	@Override
	public List<Domain> reflectDbToDomainList(String dbName,String dbType) throws Exception{
		String dbLink = TableServiceImpl.dbLink;
		String dbHost = TableServiceImpl.dbHost;
		String dbPort = TableServiceImpl.dbPort;
		String userName = TableServiceImpl.userName;
		String password = TableServiceImpl.password;
		int topn = TableServiceImpl.topn;
		if (dbType.equalsIgnoreCase("MariaDB")||dbType.equalsIgnoreCase("MySQL")) {
			try (Connection connection = DBConf.initDB(dbHost,dbPort,dbName,userName,password)) {
				List<Table> tables = dao.listAllTables(connection);
				List<Domain> domainList = new ArrayList<>();
				for (Table t:tables) {
					Domain d = reflectTableToDomain(t.getTableName(),dbType);	
					List<Domain> dataDomains = dao.fetchDataDomains(connection, d, t.getTableName(), topn);
					dataDomains = ReflectUtil.decorateDomainNamesFromRawDomains(dataDomains);
					dataDomains = ReflectUtil.decorateDomainMetaFieldsFromRawDomains(dataDomains,dbType);
					d.setDummyDb(dataDomains);
					domainList.add(d);
				}
				domainList = ReflectUtil.decorateDomainNamesFromRawDomains(domainList);
				domainList = ReflectUtil.decorateDomainMetaFieldsFromRawDomains(domainList,dbType);
				domainList = ReflectUtil.decorateManyToManiesFromRawDomains(domainList);
				domainList = ReflectUtil.decorateDropdownsFromRawDomains(domainList);
				return domainList;
			}
		} else if (dbType.equalsIgnoreCase("PostgreSQL")||dbType.equalsIgnoreCase("pgsql")) {
			try (Connection connection = DBConf.initPgDB(dbHost,dbPort,dbName,userName,password)) {
				List<Table> tables = dao.listAllPgTables(connection);
				List<Domain> domainList = new ArrayList<>();
				for (Table t:tables) {
					Domain d = reflectTableToDomain(t.getTableName(),dbType);
					List<Domain> dataDomains = dao.fetchDataDomains(connection, d, t.getTableName(),topn);
					dataDomains = ReflectUtil.decorateDomainNamesFromRawDomains(dataDomains);
					dataDomains = ReflectUtil.decorateDomainMetaFieldsFromRawDomains(dataDomains,dbType);
					d.setDummyDb(dataDomains);
					domainList.add(d);
				}
				domainList = ReflectUtil.decorateDomainNamesFromRawDomains(domainList);
				domainList = ReflectUtil.decorateDomainMetaFieldsFromRawDomains(domainList,dbType);
				domainList = ReflectUtil.decorateManyToManiesFromRawDomains(domainList);
				domainList = ReflectUtil.decorateDropdownsFromRawDomains(domainList);
				System.out.println(domainList.get(0).findFieldByFieldName("userId").getFieldType());
				System.out.println(domainList.get(0).getDummyDb().get(0).findFieldByFieldName("userId").getFieldType());
				System.out.println(domainList.get(0).getDummyDb().get(0).findFieldByFieldName("userId") instanceof Dropdown);
				return domainList;
			}
		}
		return new ArrayList<>();
	}

	@Override
	public List<Table> listAllPgTables() throws Exception {
		String dbLink = TableServiceImpl.dbLink;
		String dbHost = TableServiceImpl.dbHost;
		String dbPort = TableServiceImpl.dbPort;
		String dbName = TableServiceImpl.dbName;
		String userName = TableServiceImpl.userName;
		String password = TableServiceImpl.password;
		try (Connection connection = DBConf.initPgDB(dbHost,dbPort,dbName,userName,password)) {
			return dao.listAllPgTables(connection);
		}
	}

	@Override
	public List<Domain> fetchDataDomains(String tableName, String dbType) throws Exception {
		String dbLink = TableServiceImpl.dbLink;
		String dbHost = TableServiceImpl.dbHost;
		String dbPort = TableServiceImpl.dbPort;
		String userName = TableServiceImpl.userName;
		String password = TableServiceImpl.password;
		int topn = TableServiceImpl.topn;
		if (dbType.equalsIgnoreCase("MariaDB")||dbType.equalsIgnoreCase("MySQL")) {
			try (Connection connection = DBConf.initDB(dbHost,dbPort,dbName,userName,password)) {
				Domain d = reflectTableToDomain(tableName,dbType);
				List<Domain> dataDomains = dao.fetchDataDomains(connection, d, tableName,topn);
				return dataDomains;
			}
		}
		return null;
	}

	@Override
	public SimpleAuthModule reflectSimpleAuthModule(List<Domain> domains, List<List<Domain>> datadomains){
		try {
			if (domains == null || domains.size() == 0) return null;
			Domain userDomain = DomainUtil.findDomainInListReturnNull(domains, "User");
			Domain roleDomain = DomainUtil.findDomainInListReturnNull(domains, "Role");
			Domain privilegeDomain = DomainUtil.findDomainInListReturnNull(domains, "Privilege");
			if (userDomain == null ||roleDomain == null || privilegeDomain == null) return null;
			else {
				org.light.domain.Field userNameField = userDomain.findFieldByFieldName("userName");
				org.light.domain.Field passwordField = userDomain.findFieldByFieldName("password");
				org.light.domain.Field saltField = userDomain.findFieldByFieldName("salt");
				org.light.domain.Field loginFailureField = userDomain.findFieldByFieldName("loginFailure");
				ManyToMany mtm0 = userDomain.findManyToManyByStandardName("Role");
				ManyToMany mtm1 = roleDomain.findManyToManyByStandardName("Privilege");
				if (userNameField == null || passwordField == null || saltField == null || loginFailureField == null || mtm0 == null || mtm1 == null) return null;
				else if (!userNameField.getFieldType().equalsIgnoreCase("String") || !passwordField.getFieldType().equalsIgnoreCase("String") || !saltField.getFieldType().equalsIgnoreCase("String") || !loginFailureField.getFieldType().equalsIgnoreCase("i32")) {
					return null;
				} else {
					SimpleAuthModule sam = new SimpleAuthModule();
					sam.setStandardName("SimpleAuth");
					sam.setUserDomain(userDomain);
					sam.setRoleDomain(roleDomain);
					sam.setPrivilegeDomain(privilegeDomain);
					return sam;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public ShiroAuthModule reflectShiroAuthModule(List<Domain> domains, List<List<Domain>> datadomains){
		try {
			if (domains == null || domains.size() == 0) return null;
			Domain userDomain = DomainUtil.findDomainInListReturnNull(domains, "User");
			Domain roleDomain = DomainUtil.findDomainInListReturnNull(domains, "Role");
			Domain privilegeDomain = DomainUtil.findDomainInListReturnNull(domains, "Privilege");
			if (userDomain == null ||roleDomain == null || privilegeDomain == null) return null;
			else {
				org.light.domain.Field userNameField = userDomain.findFieldByFieldName("userName");
				org.light.domain.Field passwordField = userDomain.findFieldByFieldName("password");
				org.light.domain.Field saltField = userDomain.findFieldByFieldName("salt");
				org.light.domain.Field loginFailureField = userDomain.findFieldByFieldName("loginFailure");
				ManyToMany mtm0 = userDomain.findManyToManyByStandardName("Role");
				ManyToMany mtm1 = roleDomain.findManyToManyByStandardName("Privilege");
				if (userNameField == null || passwordField == null || saltField == null || loginFailureField == null || mtm0 == null || mtm1 == null) return null;
				else if (!userNameField.getFieldType().equalsIgnoreCase("String") || !passwordField.getFieldType().equalsIgnoreCase("String") || !saltField.getFieldType().equalsIgnoreCase("String") || !loginFailureField.getFieldType().equalsIgnoreCase("i32")) {
					return null;
				} else {
					ShiroAuthModule sam = new ShiroAuthModule();
					sam.setUserDomain(userDomain);
					sam.setRoleDomain(roleDomain);
					sam.setPrivilegeDomain(privilegeDomain);
					sam.setStandardName("ShiroAuth");
					return sam;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
